package com.world.rickcloudendpoint.service.net.websocket;

import com.world.rickcloudendpoint.common.constant.GlobalConstants;
import com.world.rickcloudendpoint.service.net.factory.NettyThreadFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;

/**
 * @description: web socket 服务
 * @author: jlsong
 * @date: 2021/12/08 21:03:16
 * @version: 1.0
 */
@Log4j2
@Component
public class WebSocketServer {

    /**
     * 通道初始化器
     */
    final
    SocketChannelInitializer socketChannelInitializer;

    public WebSocketServer(SocketChannelInitializer socketChannelInitializer) {
        this.socketChannelInitializer = socketChannelInitializer;
    }


    /**
     * boss 线程组用于处理连接工作
     */
    private EventLoopGroup boss = new NioEventLoopGroup(1, new NettyThreadFactory("web-socket-server-boss"));
    /**
     * work 线程组用于数据处理
     */
    private EventLoopGroup work = new NioEventLoopGroup(1, new NettyThreadFactory("web-socket-server-worker"));

    @Value("${netty.websocket.port}")
    private Integer port;



    /**
     * 启动Netty Server
     */
//    @PostConstruct
    public void start() {
        try {
            log.info(String.format("%s开始启动  web socket server ...", GlobalConstants.WEB_SOCKET_LOG_ID));
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, work)
                    // 指定Channel
                    .channel(NioServerSocketChannel.class)
                    //使用指定的端口设置套接字地址
                    .localAddress(new InetSocketAddress(port))

                    //服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数
                    .option(ChannelOption.SO_BACKLOG, 1024)

                    //设置TCP长连接,一般如果两个小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
                    .childOption(ChannelOption.SO_KEEPALIVE, true)

                    //将小的数据包包装成更大的帧进行传送，提高网络的负载
                    .childOption(ChannelOption.TCP_NODELAY, true)

                    .childHandler(socketChannelInitializer);

            // 绑定端口，开始接收进来的连接
            ChannelFuture future = bootstrap.bind().sync();
            if (future.isSuccess()) {
                log.info(String.format("%s启动  web socket server 成功，正常监听端口->%s ", GlobalConstants.WEB_SOCKET_LOG_ID, port));
            }
            // 等待服务器socket关闭
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            log.error(String.format("%s启动  web socket server 异常，异常原因->%s ", GlobalConstants.WEB_SOCKET_LOG_ID, e.getMessage()), e);
        } finally {
            destroy();
        }
    }

//    @PreDestroy
    public void destroy() {
        try {
            boss.shutdownGracefully().sync();
            work.shutdownGracefully().sync();
            log.info(String.format("%s关闭  web socket server  成功", GlobalConstants.WEB_SOCKET_LOG_ID));
        } catch (Exception e) {
            log.error(String.format("%s关闭  web socket server 异常，异常原因->%s ", GlobalConstants.WEB_SOCKET_LOG_ID, e.getMessage()), e);
        }
    }


}
